#include "graphics/animation.h"

namespace dw
{
	namespace graphics
	{
		Animation::Animation() : _interpolationMode(LINEAR) 
		{
			_currentPosKey = _posKeys.begin();
			_currentRotKey = _rotKeys.begin();
			_currentScaleKey = _scaleKeys.begin();
		}

		Animation::Animation(const std::string& name) : 
			_name(name), _interpolationMode(LINEAR)
		{
			_currentPosKey = _posKeys.begin();
			_currentRotKey = _rotKeys.begin();
			_currentScaleKey = _scaleKeys.begin();
		}

		Point3f Animation::getPosition(const float& t)
		{
			_currentPosKey = _posKeys.begin();
			PosKeyMap::iterator posKeyIter = _currentPosKey;
			float minTime			= _currentPosKey->first;
			float maxTime			= _posKeys.rbegin()->first;
			float sampleTime	= t;

			sampleTime = t - (float(::floor(sampleTime / maxTime)) * maxTime);

			if (sampleTime < minTime)       sampleTime = minTime;
			else if (sampleTime >= maxTime) sampleTime = maxTime - 0.001f;

			Key< Point3f > key0 = (--_posKeys.upper_bound(sampleTime))->second;
			Key< Point3f > key1 = _posKeys.upper_bound(sampleTime)->second;

			_currentPosKey = posKeyIter;

			float u;
			if (key1.time > key0.time) 
				u = (sampleTime - key0.time) / (key1.time - key0.time);
			else u = 1;

			return maths::lerp(key0.value, key1.value, u);
		}

		Quaternionf Animation::getRotation(const float& t)
		{
			_currentRotKey = _rotKeys.begin();
			RotKeyMap::iterator rotKeyIter = _currentRotKey;
			float minTime			= _currentRotKey->first;
			float maxTime			= _rotKeys.rbegin()->first;
			float sampleTime	= t;

			sampleTime = t - (float(floor(sampleTime / maxTime)) * maxTime);

			if (sampleTime < minTime)       sampleTime = minTime;
			else if (sampleTime >= maxTime) sampleTime = maxTime - 0.001f;

			Key< Quaternionf > key0 = (--_rotKeys.upper_bound(sampleTime))->second;
			Key< Quaternionf > key1 = _rotKeys.upper_bound(sampleTime)->second;

			_currentRotKey = rotKeyIter;

			float u;
			if (key1.time > key0.time) 
				u = (sampleTime - key0.time) / (key1.time - key0.time);
			else u = 1;

			return Quaternionf::slerp(key0.value, key1.value, u);
		}

		Vector3f Animation::getScale(const float& t)
		{
			_currentScaleKey = _scaleKeys.begin();
			ScaleKeyMap::iterator scaleKeyIter = _currentScaleKey;
			float minTime			= _currentScaleKey->first;
			float maxTime			= _scaleKeys.rbegin()->first;
			float sampleTime	= t;

			sampleTime = t - (float(floor(sampleTime / maxTime)) * maxTime);

			if (sampleTime < minTime)       sampleTime = minTime;
			else if (sampleTime >= maxTime) sampleTime = maxTime - 0.001f;

			Key< Vector3f > key0 = (--_scaleKeys.upper_bound(sampleTime))->second;
			Key< Vector3f > key1 = _scaleKeys.upper_bound(sampleTime)->second;

			_currentScaleKey = scaleKeyIter;

			float u;
			if (key1.time > key0.time) 
				u = (sampleTime - key0.time) / (key1.time - key0.time);
			else u = 1;

			return maths::lerp(key0.value, key1.value, u);
		}

		void Animation::addPosKey(const float& t, const Point3f& p)
		{
			Key< Point3f > key;
			key.time = t;
			key.value = p;
			_posKeys.insert(std::make_pair(t, key));
		}

		void Animation::addRotKey(const float& t, const Quaternionf& q)
		{
			Key< Quaternionf > key;
			key.time = t;
			key.value = q;
			_rotKeys.insert(std::make_pair(t, key));
		}

		void Animation::addScaleKey(const float& t, const Vector3f& v)
		{
			Key< Vector3f > key;
			key.time = t;
			key.value = v;
			_scaleKeys.insert(std::make_pair(t, key));
		}

		
		std::ostream& operator<<	(std::ostream& os, const Animation& animation)
		{
			unsigned nameLength	= static_cast< unsigned >(animation._name.size());
			unsigned numPosKeys		= animation.getNumPosKeys();
			unsigned numRotKeys		= animation.getNumRotKeys();
			unsigned numScaleKeys	= animation.getNumScaleKeys();

			Animation::PosKeyMap::const_iterator posKeyIter;
			Animation::RotKeyMap::const_iterator rotKeyIter;
			Animation::ScaleKeyMap::const_iterator scaleKeyIter;

			os.write(reinterpret_cast< const char* >(&nameLength), sizeof (unsigned));
			os.write(animation._name.c_str(), nameLength + 1);

			os.write(reinterpret_cast< const char* >(&numPosKeys), sizeof (unsigned));
			for (posKeyIter = animation._posKeys.begin(); posKeyIter != animation._posKeys.end(); ++posKeyIter)
			{
				os.write(reinterpret_cast< const char* >(&(posKeyIter->first)), sizeof (float));
				os.write(reinterpret_cast< const char* >(&(posKeyIter->second)), sizeof (Animation::Key< Point3f >));
			}
			os.write(reinterpret_cast< const char* >(&numRotKeys), sizeof (unsigned));
			for (rotKeyIter = animation._rotKeys.begin(); rotKeyIter != animation._rotKeys.end(); ++rotKeyIter)
			{
				os.write(reinterpret_cast< const char* >(&(rotKeyIter->first)), sizeof (float));
				os.write(reinterpret_cast< const char* >(&(rotKeyIter->second)), sizeof (Animation::Key< Quaternionf >));
			}
			os.write(reinterpret_cast< const char* >(&numScaleKeys), sizeof (unsigned));
			for (scaleKeyIter = animation._scaleKeys.begin(); scaleKeyIter != animation._scaleKeys.end(); ++scaleKeyIter)
			{
				os.write(reinterpret_cast< const char* >(&(scaleKeyIter->first)), sizeof (float));
				os.write(reinterpret_cast< const char* >(&(scaleKeyIter->second)), sizeof (Animation::Key< Vector3f >));
			}
			return os;
		}
			
		
		std::istream& operator>>	(std::istream& is, Animation& animation)
		{
			char* name;
			unsigned nameLength;
			unsigned numPosKeys;
			unsigned numRotKeys;
			unsigned numScaleKeys;

			is.read(reinterpret_cast< char* >(&nameLength), sizeof (unsigned));
			name = new char[nameLength + 1];
			is.read(name, nameLength + 1);
			animation._name.assign(name, nameLength);
			delete [] name;

			is.read(reinterpret_cast< char* >(&numPosKeys), sizeof (unsigned));
			for (unsigned i = 0; i < numPosKeys; ++i)
			{
				float t;
				Animation::Key< Point3f > posKey;
				is.read(reinterpret_cast< char* >(&t), sizeof (float));
				is.read(reinterpret_cast< char* >(&posKey), sizeof (Animation::Key< Point3f >));
				animation.addPosKey(t, posKey.value);
			}
			is.read(reinterpret_cast< char* >(&numRotKeys), sizeof (unsigned));
			for (unsigned i = 0; i < numRotKeys; ++i)
			{
				float t;
				Animation::Key< Quaternionf > rotKey;
				is.read(reinterpret_cast< char* >(&t), sizeof (float));
				is.read(reinterpret_cast< char* >(&rotKey), sizeof (Animation::Key< Quaternionf >));
				animation.addRotKey(t, rotKey.value);
			}
			is.read(reinterpret_cast< char* >(&numScaleKeys), sizeof (unsigned));
			for (unsigned i = 0; i < numScaleKeys; ++i)
			{
				float t;
				Animation::Key< Vector3f > scaleKey;
				is.read(reinterpret_cast< char* >(&t), sizeof (float));
				is.read(reinterpret_cast< char* >(&scaleKey), sizeof (Animation::Key< Vector3f >));
				animation.addScaleKey(t, scaleKey.value);
			}
			return is;
		}
	}
}